import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'
import { Router, ActivatedRoute } from '@angular/router'
import { Observable, BehaviorSubject, of, timer } from 'rxjs'
import { map, debounceTime, switchMap } from 'rxjs/operators'
import {
  NzFormatEmitEvent,
  NzTreeNode,
  NzDropdownService,
  NzDropdownContextComponent,
  NzTreeNodeOptions,
  NzTreeComponent,
} from 'ng-zorro-antd'
import { ACLService } from '@delon/acl'

import { MenuDataService } from '../menu-data.service'
import { IMenu, IMenuOrdinal } from '@shared/interfaces/role'
import { MenuService } from '../menu.service'
import { TreeService } from '@shared/services/tree.service'

@Component({
  selector: 'app-menu-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.less'],
})
export class MenuListComponent implements OnInit {
  constructor(
    public aclService: ACLService,
    private router: Router,
    private route: ActivatedRoute,
    private menuService: MenuService,
    private menuDataService: MenuDataService,
    private nzDropdownService: NzDropdownService,
    private treeService: TreeService,
  ) {}

  private dropdown: NzDropdownContextComponent

  isLoading = false
  searchValue = ''
  node: NzTreeNode
  nodes$: Observable<NzTreeNodeOptions[]>
  nodes: NzTreeNode[] | NzTreeNodeOptions[]
  menuParams: boolean | IMenu[]
  loadMenus$ = new BehaviorSubject(this.menuParams)
  _menus: IMenu[] = []
  expandedKeys: string[] = []
  _expandedKeys: string[] = []

  @ViewChild('tree') tree: NzTreeComponent
  @ViewChild('tplMenu') tplMenu: TemplateRef<any>

  ngOnInit() {
    this.isLoading = true

    this.loadMenus$
      .asObservable()
      .pipe(
        debounceTime(500),
        switchMap(params => {
          if (typeof params === 'boolean') {
            return this.menuService.getMenus()
          }

          return of(params)
        }),
        map(menus => {
          this._menus = menus.slice(0)
          return this.menuDataService.createOptionTree(menus)
        }),
      )
      .subscribe(nodes => {
        this.nodes = nodes
        this.expandedKeys = [...this._expandedKeys]
        this.isLoading = false
      })

    this.loadMenus$.next(false)

    this.route.paramMap.pipe(map(() => window.history.state)).subscribe(value => {
      if (value.isMenuCreated === true) {
        // load menus from server because a new menu was created
        this.loadMenus$.next(true)
      }
    })
  }

  onNodeDropEvent(event: NzFormatEmitEvent): void {
    this.isLoading = true
    const menus = this.treeService.preOrderTraverse(this.tree.getTreeNodes())

    const data: IMenuOrdinal[] = menus.map((m, idx) => ({
      menuId: m.origin.menuId,
      level: m.level + 1,
      ordinal: idx,
    }))

    this.menuService.resetAll(data).subscribe(
      () => {
        this.isLoading = false
      },
      () => {
        this.isLoading = false
      },
    )
  }

  onContextClick(event: NzFormatEmitEvent, template: TemplateRef<void>) {
    this.node = event.node
    this.dropdown = this.nzDropdownService.create(event.event, template)
  }

  onMenuCopy() {
    this.isLoading = true
    const node = this.node
    this._expandedKeys = this.menuDataService.getAncestors(node).map(n => n.key)
    const menus = this.menuDataService.copyMenu(node.origin.menuOrdinal, this._menus)

    // post data to server
    const data: IMenuOrdinal[] = menus.map(m => ({
      menuId: m.menuId,
      level: m.level,
      ordinal: m.ordinal,
    }))
    this.menuService.resetAll(data).subscribe(
      resp => {
        // 保存成功后，需要把返回的数据更新到页面
        this.loadMenus$.next(menus)
      },
      () => {
        this.isLoading = false
      },
    )

    this.closeDropdown()
  }

  onMenuRemove() {
    this.isLoading = true
    const node = this.node
    this._expandedKeys = this.menuDataService.getAncestors(node).map(n => n.key)
    const menus = this.menuDataService.removeMenu(node.origin.menuOrdinal, this._menus)

    // post data to server
    const data: IMenuOrdinal[] = menus.map(m => ({
      menuId: m.menuId,
      level: m.level,
      ordinal: m.ordinal,
    }))
    this.menuService.resetAll(data).subscribe(
      resp => {
        this.loadMenus$.next(menus)
      },
      () => {
        this.isLoading = false
      },
    )

    this.closeDropdown()
  }

  onMenuPromote() {
    this.node.isLeaf = false
    this.node.children = []
    this.isLoading = true
    timer(1000).subscribe(() => {
      this.closeDropdown()
      this.isLoading = false
    })
  }

  closeDropdown(): void {
    this.dropdown.close()
  }

  goNew(): void {
    this.router.navigate(['new'], {
      relativeTo: this.route,
    })
  }
}
